package middleware

import (
	"ChdChat-Gin/initialize"
	"ChdChat-Gin/model"
	"ChdChat-Gin/utils"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	"log"
	"net/http"
	"strings"
)

var (
	ErrNotAuth         = errors.New("user does not authenticates")
	ErrDecrypt         = errors.New("failed to decrypt authentication information")
	ErrIllegalAuthInfo = errors.New("illegal authentication information")
)

func Authenticate() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenStr := c.Request.Header.Get("Authorization")
		claims, err := parseBearerToken(tokenStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"msg": "Failed to parse token",
			})
			log.Printf("Authenticate.parseBearerToken:%v\n", err)
			return
		}
		userinfo, err := entityValidation(claims)
		if err != nil {
			switch err {
			case ErrNotAuth:
				c.JSON(http.StatusUnauthorized, gin.H{
					"msg": "You have not logged in or your login status has expired. Please log in again",
				})
				c.Abort()
			case ErrIllegalAuthInfo:
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "Authentication failed, your account will be banned",
				})
				c.Abort()
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "Authentication failed, please try again",
				})
				c.Abort()
			}
			log.Printf("Authenticate.entityValidation:%v\n", err)
			return
		}
		c.Set("claims", claims)
		c.Set("userInfo", userinfo)
		c.Next()
	}
}

func parseBearerToken(tokenStr string) (*model.Claims, error) {
	if tokenStr == "" {
		return nil, errors.New("token is empty")
	}
	splitToken := strings.Split(tokenStr, "Bearer ")
	if len(splitToken) != 2 && splitToken[0] != "Bearer " {
		return nil, errors.New("token format error")
	}
	claims, err := utils.ParseToken(splitToken[1])
	if err != nil {
		log.Printf("utils.ParseToken.%v", err)
		return nil, errors.New("failed to parse token")
	}
	return claims, nil
}

func entityValidation(claims *model.Claims) (model.UserInfos, error) {
	redisConn := initialize.RedisPool.Get()
	defer func(redisConn redis.Conn) {
		if err := redisConn.Close(); err != nil {
			panic(err)
		}
	}(redisConn)
	// 从redis中获取认证信息
	marshaled, err := redis.String(redisConn.Do("GET", claims.RdsKey))
	if err != nil {
		log.Printf("redis.String:%v\n", err)
		// 查找不到的原因：未登录、rdsKey被修改、认证信息过期
		return model.UserInfos{}, ErrNotAuth
	}
	// 解析认证信息为json格式
	rdsVal := struct {
		Encoded  string          `json:"encoded"`
		ClientIp string          `json:"clientIp"`
		UserInfo model.UserInfos `json:"userInfo"`
	}{}
	if err = json.Unmarshal([]byte(marshaled), &rdsVal); err != nil {
		log.Printf("json.Unmarshal:%v\n", err)
		return model.UserInfos{}, ErrDecrypt
	}
	// Base64解码密文
	cipherBytes, err := base64.StdEncoding.DecodeString(rdsVal.Encoded)
	if err != nil {
		log.Printf("base64.DcodeString:%v\n", err)
		return model.UserInfos{}, ErrDecrypt
	}
	// 创建AES解密器
	aesKey, err := hex.DecodeString(claims.AesKey)
	if err != nil {
		log.Printf("hex.DecodeString:%v\n", err)
		return model.UserInfos{}, ErrDecrypt
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		log.Printf("aes.NewCipher:%v\n", err)
		return model.UserInfos{}, ErrDecrypt
	}
	// 解密
	iv := cipherBytes[:aes.BlockSize]
	cipherText := cipherBytes[aes.BlockSize:]
	blockMode := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(cipherText))
	blockMode.CryptBlocks(plaintext, cipherText)
	// 去除填充字符
	padding := int(plaintext[len(plaintext)-1])
	plaintext = plaintext[:len(plaintext)-padding]

	if string(plaintext) != claims.Account {
		log.Println("Authentication illegal")
		return model.UserInfos{}, ErrIllegalAuthInfo
	}
	return rdsVal.UserInfo, nil
}

// CheckAccountStatus 检查当前登录账号的状态并做相应处理，例如异地重复登录禁止
//func CheckAccountStatus() gin.HandlerFunc {
//	return func(c *gin.Context) {
//		var loginBody model.UserAccounts
//		if err1 := c.BindJSON(&loginBody); err1 != nil {
//			c.JSON(http.StatusBadRequest, gin.H{
//				"msg":  "Parameter format error",
//				"data": err1,
//			})
//			c.Abort()
//			return
//		}
//		c.Set("loginBody", loginBody)
//		claims := parseToken(c)
//
//		redisConn := initialize.RedisPool.Get()
//		defer func(redisConn redis.Conn) {
//			if err := redisConn.Close(); err != nil {
//				panic(err)
//			}
//		}(redisConn)
//		// 从redis中获取认证信息
//		marshaled, err := redis.String(redisConn.Do("GET", claims.RdsKey))
//		if err != nil || marshaled == nil {
//			c.Next()
//			return
//		}
//		rdsVal := struct {
//			Encoded  string          `json:"encoded"`
//			ClientIp string          `json:"clientIp"`
//			UserInfo model.UserInfos `json:"userInfo"`
//		}{}
//		err = json.Unmarshal([]byte(marshaled), &rdsVal)
//		if err != nil {
//			return
//		}
//		if loginBody.Account != rdsVal.UserInfo.Account {
//			c.Next()
//			return
//		}
//		ip := c.ClientIP()
//		if ip == rdsVal.ClientIp {
//			c.JSON(http.StatusOK, gin.H{
//				"msg": "该账号已登录",
//			})
//			c.Abort()
//		} else {
//			c.JSON(http.StatusOK, gin.H{
//				"msg": "该账号已在其他地方登录，无法重复登录",
//			})
//			c.Abort()
//		}
//	}
//}

//func AuthWebsocket() gin.HandlerFunc {
//	return func(c *gin.Context) {
//		claims := checkToken(c)
//		// 存储 websocket.Conn 对象到 Gin 上下文中
//		wsConn, err := (&websocket.Upgrader{
//			//Subprotocols: []string{token},
//			CheckOrigin: func(r *http.Request) bool {
//				return true
//			},
//		}).Upgrade(c.Writer, c.Request, nil)
//		if err != nil {
//			c.JSON(http.StatusInternalServerError, gin.H{
//				"msg":  "Failed to connect to chat room",
//				"data": err,
//			})
//			panic(err)
//		}
//		fmt.Println(claims)
//		ctx := context.WithValue(c, "claims", claims)
//		ctx = context.WithValue(ctx, "wsConn", wsConn)
//		c.Set("gin-context", ctx)
//	}
//}
